﻿/*              Copyright(c) 2008 Dave Sexton              *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace DaveSexton.DocToMaml
{
	partial class ProgramForm
	{
		partial class ProgramFormUIStateManager : UIStateManagerBase
		{
			#region Public Properties
			public IInput SelectedInput
			{
				get
				{
					ListView.SelectedListViewItemCollection items = form.inputListView.SelectedItems;

					if (items.Count != 1)
						return null;
					else
						return (IInput) items[0].Tag;
				}
				set
				{
					form.inputListView.BeginUpdate();

					try
					{
						foreach (ListViewItem item in form.inputListView.Items)
						{
							if (item.Tag == value)
							{
								form.inputListView.SelectedItems.Clear();
								item.Selected = true;
								return;
							}
						}

						throw new ArgumentException(Resources.Errors.InputNotFound, "value");
					}
					finally
					{
						form.inputListView.EndUpdate();
					}
				}
			}
			#endregion

			#region Private / Protected
			private bool ignoreContentsEditorChanges, ignoreResultEditorChanges, ignoreRulesBindingSourceChangeEvents, resultChanged;
			private FileInput fileInputInEditMode;
			private ProjectManager project;
			#endregion

			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="ProgramFormController" /> class.
			/// </summary>
			public ProgramFormUIStateManager(ProgramForm form)
				: base(form)
			{
				this.project = form.project;
			}
			#endregion

			#region Methods
			/// <summary>
			/// Visual Studio is terrible when it comes to designing toolstrips - they are automatically rearranged every time the designer is opened.
			/// The workaround is to explicitly re-join them in reverse order, from right to left.  Each row may be reset independently of the other rows.
			/// </summary>
			protected override void ResetCustomToolStrips()
			{
				form.inputToolStrip.Parent.Controls.Remove(form.inputToolStrip);
				form.editorToolStrip.Parent.Controls.Remove(form.editorToolStrip);

				// re-join in reverse order (from right to left) for each row
				form.toolStrips.TopToolStripPanel.Join(form.editorToolStrip, 1);
				form.toolStrips.TopToolStripPanel.Join(form.inputToolStrip, 1);
			}

			protected override void UpdateCustomToolStripState(bool projectLoaded)
			{
				form.inputToolStrip.Enabled = projectLoaded;
				SetEditorState(false);		// editor is always disabled by default, regardless of whether a project is loaded or unloaded
			}

			protected override void BindToProject()
			{
				form.inputListView.BeginUpdate();

				try
				{
					form.inputListView.Items.Clear();

					foreach (IInput input in project.Inputs)
					{
						if (input is FolderInput)
							form.AddFolderInput(input);
						else
							form.AddFileInput(input);
					}
				}
				finally
				{
					form.inputListView.EndUpdate();
				}

				form.convertToolStripButton.Enabled = form.inputListView.Items.Count > 0;
				form.convertAllToolStripMenuItem.Enabled = form.convertToolStripButton.Enabled;

				if (!string.IsNullOrEmpty(project.LastOutputDirectory))
				{
					form.openResultsFolderToolStripButton.Enabled = true;
					form.openResultsFolderToolStripMenuItem.Enabled = true;
				}

				form.contentTabs.Enabled = true;

				SetRulesDataGridViewState(true);

				UpdateForSelectedInput();
			}

			protected override void UnbindFromProject()
			{
				form.inputListView.Items.Clear();

				form.openResultsFolderToolStripButton.Enabled = false;
				form.openResultsFolderToolStripMenuItem.Enabled = false;
				form.convertToolStripButton.Enabled = false;
				form.convertAllToolStripMenuItem.Enabled = false;
				form.rulesDataGridView.Tag = null;
				form.contentTabs.Enabled = false;

				SetRulesDataGridViewState(false);

				form.logTextBox.Text = string.Empty;
				form.imagesTextBox.Text = string.Empty;
			}

			public void UpdateForSelectedInput()
			{
				if (form.convertingAsync)
					return;

				form.deleteInputToolStripButton.Enabled = form.inputListView.SelectedItems.Count > 0;

				IInput input = SelectedInput;

				UpdateRulesDataGridView(input);

				UpdateForSelectedTab(input);
			}

			public void UpdateForSelectedTab()
			{
				UpdateForSelectedTab(SelectedInput);
			}

			public void UpdateForSelectedTab(IInput input)
			{
				UpdateFileWithEditorChanges();

				if (form.contentTabs.SelectedTab == form.logTabPage)
					UpdateLogTab(input);
				else if (form.contentTabs.SelectedTab == form.imagesTabPage)
					UpdateImagesTab(input);

				FileInput file = input as FileInput;

				bool showingSource = false;

				if (file == null)
				{
					fileInputInEditMode = null;

					SetEditorState(false);
				}
				else
				{
					if (form.contentTabs.SelectedTab == form.resultTabPage)
						UpdateResultsTab(file);
					else if (form.contentTabs.SelectedTab == form.sourceTabPage)
					{
						UpdateSourceTab(file);
						showingSource = true;
					}
				}

				if (!showingSource)
					form.editorToolStrip.Enabled = false;
			}

			private void UpdateLogTab(IInput input)
			{
				form.logTextBox.Text = (input == null)
					? project.Project.ConversionLog
					: input.Log;
			}

			private void UpdateImagesTab(IInput input)
			{
				form.imagesTextBox.Text = (input == null)
					? project.Images.CreateConceptualArtFileXml()
					: input.ConceptualArtFileXml;
			}

			private void UpdateResultsTab(FileInput file)
			{
				if (!form.editor.IsDirty)
					fileInputInEditMode = null;

				ignoreResultEditorChanges = true;

				try
				{
					if (!file.HasResult)
						project.EnsureInputGuids();

					form.resultTextBox.Text = file.Result;
				}
				catch (HtmlToMamlConversionException ex)
				{
					// set the conversion error so that it appears in the log
					file.SetError(ex);

					form.contentTabs.SelectedTab = form.logTabPage;

					form.resultTextBox.Text = string.Empty;
				}
				finally
				{
					ignoreResultEditorChanges = false;
				}

				SetEditorState(true);
			}

			private void UpdateSourceTab(FileInput file)
			{
				fileInputInEditMode = file;

				ignoreContentsEditorChanges = true;

				try
				{
					form.editor.Html = file.Contents;

					System.Diagnostics.Debug.WriteLine("HTML set for file: " + file.Name, "ProgramForm Source View");
				}
				catch (System.IO.FileNotFoundException ex)
				{
					// set the conversion error so that it appears in the log
					file.SetError(ex);

					form.contentTabs.SelectedTab = form.logTabPage;

					form.editor.Html = string.Empty;

					System.Diagnostics.Debug.WriteLine("Missing file: " + file.Name, "ProgramForm Source View");
				}
				finally
				{
					ignoreContentsEditorChanges = false;
				}

				if (file.ResultModified)
				{
					form.sourceResultChangedWarningButton.Visible = true;
					SetEditorState(false, false);
				}
				else
					SetEditorState(true);
			}

			private void SetEditorState(bool enabled)
			{
				SetEditorState(enabled, true);
			}

			private void SetEditorState(bool enabled, bool clearSource)
			{
				form.resultTextBox.Enabled = enabled;
				form.editorPanel.Enabled = enabled;
				form.editorToolStrip.Enabled = enabled;

				if (!enabled && clearSource)
				{
					form.sourceResultChangedWarningButton.Visible = false;

					ignoreContentsEditorChanges = true;
					ignoreResultEditorChanges = true;

					try
					{
						form.resultTextBox.Text = string.Empty;

						if (form.editor.IsReady)
						{
							form.editor.Document.Body.InnerHtml = string.Empty;
							form.editor.IsDirty = false;
						}
					}
					finally
					{
						ignoreResultEditorChanges = false;
						ignoreContentsEditorChanges = false;
					}
				}
			}

			public void EnableSourceEditing()
			{
				FileInput input = (FileInput) SelectedInput;
				input.ResultModified = false;

				form.sourceResultChangedWarningButton.Visible = false;

				SetEditorState(true);
			}

			/// <summary>
			/// The <see cref="DaveSexton.Controls.HtmlEditor">HtmlEditor</see> control does not always raise its <see cref="DaveSexton.Controls.HtmlEditor.HtmlChanged">HtmlChanged</see>
			/// event in time before the HTML is assigned to a newly selected item.  Therefore, this method should be called before the editor's HTML will be assigned to ensure that 
			/// no changes are lost.
			/// </summary>
			private void UpdateFileWithEditorChanges()
			{
				if (fileInputInEditMode != null && form.editor.IsReady && form.editor.IsDirty)
				{
					string html = form.editor.Html;

					fileInputInEditMode.Contents = html;
					form.editor.IsDirty = false;

					System.Diagnostics.Debug.WriteLine("File source HTML updated (lazy): " + fileInputInEditMode.Name, "ProgramForm");
				}
			}

			private void UpdateRulesDataGridView(IInput input)
			{
				if (input == null)
					UpdateRulesDataGridView(Resources.Text.GlobalRules, project.Rules);
				else
					UpdateRulesDataGridView(string.Format(System.Globalization.CultureInfo.CurrentCulture,
						Resources.Text.RulesFor, input.Name), input.Rules);
			}

			private void UpdateRulesDataGridView(string label, IRuleContainer rules)
			{
				if (form.rulesDataGridView.Tag == rules)
					return;
				else
					form.rulesDataGridView.Tag = rules;

				form.currentRulesContainerLabel.Text = label;

				ignoreRulesBindingSourceChangeEvents = true;

				try
				{
					form.rulesBindingSource.DataSource = new System.ComponentModel.BindingList<IRule>(rules);
					form.rulesBindingSource.AllowNew = true;		// explicitly set to true since it's binding to a collection of interfaces
				}
				finally
				{
					ignoreRulesBindingSourceChangeEvents = false;
				}
			}

			public void UpdateForRulesDataChanges()
			{
				// NOTE: In testing, the list changed event was raised after the project was unloaded so the Loaded property must be checked.
				if (!ignoreRulesBindingSourceChangeEvents && project.Loaded)
					project.IsDirty = true;
			}

			public void CommitSourceChanges()
			{
				if (!ignoreContentsEditorChanges)
				{
					FileInput file = SelectedInput as FileInput;

					if (file != null)
					{
						file.Contents = form.editor.Html;

						System.Diagnostics.Debug.WriteLine("File source HTML updated: " + file.Name, "ProgramForm");
					}
				}
			}

			public bool CommitResultChanges()
			{
				if (!ignoreResultEditorChanges && resultChanged)
				{
					FileInput file = SelectedInput as FileInput;

					if (file != null)
					{
						try
						{
							file.Result = form.resultTextBox.Text;

							form.errors.SetError(form.resultTextBox, null);
						}
						catch (System.Xml.XmlException ex)
						{
							form.errors.SetError(form.resultTextBox, ex.Message);
							return false;
						}
					}
				}

				resultChanged = false;
				return true;
			}

			public void UpdateStateForResultTextChanged()
			{
				if (!ignoreResultEditorChanges)
					resultChanged = true;
			}

			public void SetUserCommandStateForConversion(bool converting)
			{
				form.standardToolStrip.Enabled = !converting;
				form.inputToolStrip.Enabled = !converting;

				form.newToolStripMenuItem.Enabled = !converting;
				form.openToolStripMenuItem.Enabled = !converting;
				form.closeToolStripMenuItem.Enabled = !converting;

				form.progress.Visible = converting;

				form.newRuleTableLayoutPanel.Enabled = !converting;

				SetRulesDataGridViewState(!converting);

				if (converting)
				{
					form.progress.Value = 0;

					form.openResultsFolderToolStripButton.Enabled = true;
					form.openResultsFolderToolStripMenuItem.Enabled = true;

					if (form.contentTabs.SelectedTab == form.sourceTabPage)
						UpdateFileWithEditorChanges();

					SetEditorState(false);
				}
				else
					UpdateForSelectedInput();
			}

			private void SetRulesDataGridViewState(bool enabled)
			{
				// HACK: For some unknown reason the DataGridView shows an error dialog once for each cell in the entire grid 
				// when Enabled is set to false.  This method is a work-around.

				ignoreRulesBindingSourceChangeEvents = true;

				try
				{
					if (form.rulesDataGridView.Enabled == enabled)
						return;
					else if (!enabled)
					{
						form.rulesBindingSource.DataSource = null;
						form.rulesDataGridView.Tag = null;
					}

					form.rulesDataGridView.Enabled = enabled;
					form.newRuleTableLayoutPanel.Enabled = enabled;

					if (enabled)
						UpdateRulesDataGridView(SelectedInput);
				}
				finally
				{
					ignoreRulesBindingSourceChangeEvents = false;
				}
			}

			public void UpdateInputListViewColumn()
			{
				form.inputListViewColumnHeader.Width = form.inputListView.Width - form.inputListView.Margin.Left - form.inputListView.Margin.Right;
			}
			#endregion
		}
	}
}
